/*
 * Copyright 2009 (c) Patrick Pietens.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */
package indigo.views
{
	import indigo.control.IndigoNotice;
	import indigo.control.NotificationCenter;
	import indigo.events.PropertyChangeEvent;
	import indigo.events.ViewEvent;
	import indigo.model.ApplicationModel;
	import indigo.utils.DisposableEventDispatcher;

	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.getQualifiedClassName;

	use namespace indigo;
	/**
	 * Dispatches when the <code>View</code> is registered to the 
	 * <code>ViewLocator</code>
	 * @eventType indigo.events.ViewEvent
	 */
	[Event(name="register", type="indigo.events.ViewEvent")]
	/**
	 * Dispatches when the <code>View</code> is unregistered from the 
	 * <code>ViewLocator</code>
	 * @eventType indigo.events.ViewEvent
	 */
	[Event(name="unregister", type="indigo.events.ViewEvent")]
	/**
	 * <code>Views</code> represents visually the current state of your Indigo 
	 * application. When the <code>ModelLocator</code> changes its state, it 
	 * notifies its associated <code>Views</code> so they can react and refresh.
	 * </code>
	 * 
	 * <p>By passing an unique to the <code>View</code> class its constructor 
	 * it's automatically registered to the <code>ViewLocator</code>. Once 
	 * registered you can reference <code>Views</code> from anywhere in the 
	 * application using the <code>getView()</code> shortcut.</p>
	 * 
	 * <p><code>Views</code> will dispose itself when they are removed from 
	 * <code>Stage</code>. This means it will remove all listeners, all 
	 * references, nullify its children and unregister itelf from the 
	 * <code>ViewLocator</code>. You can disable this by setting the 
	 * autoDispose property to <i>false</i>. Always override the dispose 
	 * function in your own <code>Views</code> to remove them completely.</p>
	 * 
	 * <p>Use the Indigo Templates for FDT to quickly build a new 
	 * <code>View</code>.</p>
	 * 
	 * @example
	 * <listing version="3.0">
	 * </listing> 
	 * 
	 * @playerversion Flash 9
	 * @langversion ActionScript 3.0
	 * 
	 * @see getView
	 * @see ViewLocator
	 * @see	ViewCollection
	 */
	public class ViewController extends DisposableEventDispatcher
	{
		/**
		 * String that indicates the name of the View
		 * 
		 */
		private var _completed:Boolean = false;
		private var _mouseLeftStage:Boolean = false;
		private var _view:DisplayObject;


		/**
		 * Constructor
		 * @param String that indicates the name of the view. When the viewName 
		 * is given the View will automatically	register itself to the 
		 * ViewLocator using the name
		 * @param Boolean Creates a random colored square, so the view reveals itself
		 * even if there are no childrend added
		 */
		public function ViewController( view:DisplayObject )
		{
			super();
			_view = view;
			register();
			indigo::initialize();
		}


		indigo function initialize():void
		{
			indigo::enableController();
		}


		indigo function addedToStage( event:Event ):void
		{
			DisplayObject( view ).removeEventListener( Event.ADDED_TO_STAGE, indigo::addedToStage );

			DisplayObject( view ).stage.addEventListener( Event.MOUSE_LEAVE, indigo::stageLeave );
			DisplayObject( view ).stage.addEventListener( Event.RESIZE, indigo::stageResize );
			DisplayObject( view ).addEventListener( Event.REMOVED_FROM_STAGE, indigo::removedFromStage );
			if ( !_completed )
			{
				// If the LoaderInfo is loaded
				if ( DisplayObject( view ).loaderInfo.contentType ) this.indigo::completed();
                                        
				// Otherwise wait for it
				else DisplayObject( view ).loaderInfo.addEventListener( Event.INIT, indigo::completed );
			}

			onAddedToStage();
		}


		indigo function removedFromStage( event:Event ):void
		{
			DisplayObject( view ).addEventListener( Event.ADDED_TO_STAGE, indigo::addedToStage );

			DisplayObject( view ).removeEventListener( Event.REMOVED_FROM_STAGE, indigo::removedFromStage );
			DisplayObject( view ).stage.removeEventListener( Event.RESIZE, indigo::stageResize );
			DisplayObject( view ).stage.removeEventListener( Event.MOUSE_LEAVE, indigo::stageLeave );
			DisplayObject( view ).stage.removeEventListener( MouseEvent.MOUSE_OVER, indigo::stageEnter );

			onRemovedFromStage();
		}


		/**
		 * Executes when the <code>ExtendMovieClip</code> is completely loaded
		 * @param Event which triggers the method
		 * 
		 */
		indigo function completed( event:Event = null ):void
		{
			_completed = true;
			onReady();
		}


		/**
		 * Executes after the <code>ExtendedMovieClip</code> is completely loaded
		 * 
		 */
		public function onReady():void
		{
		}


		indigo function stageLeave( event:Event ):void
		{
			if ( DisplayObject( view ).stage != null )
			{
				DisplayObject( view ).stage.addEventListener( MouseEvent.MOUSE_OVER, indigo::stageEnter );
				_mouseLeftStage = true;
				onMouseLeavesStage();
			}
		}


		indigo function stageEnter( event:MouseEvent ):void
		{
			if ( DisplayObject( view ).stage != null )
			{
				DisplayObject( view ).stage.removeEventListener( MouseEvent.MOUSE_OVER, indigo::stageEnter );
				_mouseLeftStage = false;
				onMouseEntersStage();
			}
		}


		indigo function stageResize( event:Event ):void
		{
			onStageResize();
		}


		/**
		 * Registers a view to the ViewLocator using its unique name. After the 
		 * view is registered it can be accessed throughout the Indigo 
		 * application by using the ViewLocator.getView () method. 
		 *   
		 * When the view is already registered or another View with the same 
		 * name is registered the ViewLocator will throw an exception.
		 * 
		 * @param String that indicates the name of the View
		 * @see	ViewLocator
		 * 
		 */
		private function register():void
		{
			if ( !isRegistered )
			{
				DisplayObject( view ).name = getQualifiedClassName( view );
				// Register view
				viewLocator.registerViewController( this );
				// Dispatch event
				if ( this.hasEventListener( ViewEvent.REGISTER ) )
				{
					var myEvent:ViewEvent = new ViewEvent( ViewEvent.REGISTER );
					myEvent.viewController = this;
					this.dispatchEvent( myEvent );
				}
				this.onRegister();
			}
		}


		/**
		 * Unregisters the View from the ViewLocator. 
		 * This function is automatically called when the View is disposed.
		 * 
		 */
		private function unregister():void
		{
			if ( isRegistered )
			{
				// Unregister view
				viewLocator.unregisterViewController( this );
				// Dispatch event
				if ( this.hasEventListener( ViewEvent.UNREGISTER ) )
				{
					var myEvent:ViewEvent = new ViewEvent( ViewEvent.UNREGISTER );
					myEvent.viewController = this;
					this.dispatchEvent( myEvent );
				}
				this.onUnregister();
			}
		}


		public function sendNotification( type:String, data:* = null ):void
		{
			var myEvent:IndigoNotice = new IndigoNotice( type );
			myEvent.data = data;
			NotificationCenter.getInstance().dispatchEvent( myEvent );
		}


		public function listenToNotification( event:String, listener:Function ):void
		{
			NotificationCenter.getInstance().addEventListener( event, listener, false, 1, true );
		}


		/**
		 * Executes after the <code>View</code> is registered to the 
		 * <code>ViewLocator</code>
		 * 
		 */
		public function onRegister():void
		{
		}


		/**
		 * Executes after the <code>View</code> is unregistered from the 
		 * <code>ViewLocator</code>
		 * 
		 */
		public function onUnregister():void
		{
		}


		public function onPropertyAdded( event:PropertyChangeEvent ):void
		{
		}


		public function onPropertyChanged( event:PropertyChangeEvent ):void
		{
		}


		public function onPropertyDeleted( event:PropertyChangeEvent ):void
		{
		}


		public function onAddedToStage():void
		{
		}


		public function onRemovedFromStage():void
		{
		}


		public function onStageResize():void
		{
		}


		public function onMouseLeavesStage():void
		{
		}


		public function onMouseEntersStage():void
		{
		}


		/**
		 * Boolean that indicates the <code>ExtendedMovieClip</code> is
		 * completely loaded
		 * 
		 */
		public function get completed():Boolean
		{
			return this._completed;
		}


		/**
		 * Boolean that indicates the View is registered to the ViewLocator
		 * 
		 */
		public function get isRegistered():Boolean
		{
			return viewLocator.hasView( this );
		}


		public function get mouseLeftStage():Boolean
		{
			return _mouseLeftStage;
		}


		public function get applicationModel():ApplicationModel
		{
			return ApplicationModel.getInstance();
		}


		public function get viewLocator():ViewLocator
		{
			return ViewLocator.getInstance();
		}


		public function get view():*
		{
			return _view;
		}


		indigo function enableController():void
		{
			DisplayObject( view ).addEventListener( Event.ADDED_TO_STAGE, indigo::addedToStage );
			applicationModel.addEventListener( PropertyChangeEvent.ADDED, onPropertyAdded );
			applicationModel.addEventListener( PropertyChangeEvent.CHANGED, onPropertyChanged );
			applicationModel.addEventListener( PropertyChangeEvent.DELETED, onPropertyDeleted );
		}


		indigo function disableController():void
		{
			DisplayObject( view ).removeEventListener( Event.ADDED_TO_STAGE, indigo::addedToStage );
			DisplayObject( view ).removeEventListener( Event.REMOVED_FROM_STAGE, indigo::removedFromStage );
			applicationModel.removeEventListener( PropertyChangeEvent.ADDED, onPropertyAdded );
			applicationModel.removeEventListener( PropertyChangeEvent.CHANGED, onPropertyChanged );
			applicationModel.removeEventListener( PropertyChangeEvent.DELETED, onPropertyDeleted );
		}


		/**
		 * @inheritDoc
		 * 
		 */
		override public function dispose( event:Event = null ):void
		{
			// Disable ViewController
			indigo::disableController();
			// Unregister the View
			this.unregister();
			// Call super dispose method
			super.dispose( event );
			// Nullify properties
			_view = null;
		}
	}
}

